﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Disposables;
using System.Linq;
using System.Linq.Expressions;
using System.Windows;
using System.Windows.Controls;
using SoftSource.Common;
using SoftSource.MVVMC;
using SoftSource.Reactive;
using SoftSource.Timecard.Resources;
using SoftSource.Timecard.ViewModelInterfaces;
using SoftSource.Timecard.Web.Data;

namespace SoftSource.Timecard.Views
{
    public sealed partial class InvoicingView : UserControl, IView<IInvoicingViewModelConsume>, IDisposable
    {
        public class MessageProject : INotifyPropertyChanged
        {
            private InvoicingProject project;
            public InvoicingProject Project
            {
                get { return this.project; }
                set
                {
                    this.project = value;
                    this.PropertyChanged.Notify(() => this.Project);
                }
            }

            private string message;
            public string Message
            {
                get { return this.message; }
                set
                {
                    this.message = value;
                    this.PropertyChanged.Notify(() => this.Message);
                }
            }

            private bool invoice;
            public bool Invoice
            {
                get { return this.invoice; }
                set
                {
                    this.invoice = value;
                    this.PropertyChanged.Notify(() => this.Invoice);
                }
            }

            #region INotifyPropertyChanged Members

            public event PropertyChangedEventHandler PropertyChanged;

            #endregion
        }

        #region Fields
        private ObservableCollection<MessageProject> boundProjects;
        private Func<InvoicingProject, MessageProject> messageProjectSelector;
        private CompositeDisposable compositeDisposable;
        private bool isEditorOpen;
        #endregion Fields

        public InvoicingView()
        {
            InitializeComponent();

            this.isEditorOpen = false;
            this.compositeDisposable = new CompositeDisposable();
            this.boundProjects = new ObservableCollection<MessageProject>();
            this.messageProjectSelector = project =>
                {
                    MessageProject messageProject = new MessageProject();

                    messageProject.Project = project;
                    messageProject.Message = string.Empty;

                    return messageProject;
                };

            this.InvoicingDataGrid.ItemsSource = this.boundProjects;
            Loaded += new RoutedEventHandler(InvoicingView_Loaded);
        }

        #region Events

        void InvoicingView_Loaded(object sender, RoutedEventArgs e)
        {
            if (this.ViewModel != null)
            {
                Action rebindProjects = () => this.boundProjects.Populate(
                    this.ViewModel.Projects.SourceCollection
                    .Cast<InvoicingProject>()
                    .Select(this.messageProjectSelector)
                    .OrderBy(p => p.Project.Name));

                this.Busy.BusyContent = ApplicationStrings.LoadingMessage;
                this.Busy.IsBusy = true;

                this.ViewModel.Load // Load the data required to use this ViewModel
                    .Nullable()
                    .Do(unit => rebindProjects())
                    .Subscribe(unit => this.Busy.IsBusy = false);

                this.ViewModel.PropertyChanged += (sender2, e2) =>
                    {
                        if (e2.PropertyName == "Projects")
                        {
                            rebindProjects();
                        }
                    };
            }


            var beginningEditDisposable = this.InvoicingDataGrid
                .WhenBeginningEdit()
                .Subscribe(args =>
                    {
                        args.EventArgs.Cancel = true;
                        var summary = args.EventArgs.Row.DataContext as MessageProject;

                        if (summary != null)
                        {
                            Func<Action, UIElement> editorCreator = completed => this.ViewModel.CreateProjectEditor.TryInvoke(summary.Project.ProjectID, completed);
                            this.OpenEditor(editorCreator);
                        }
                    });

            this.compositeDisposable.Add(beginningEditDisposable);
        }

        private void SetBillDates_Click(object sender, RoutedEventArgs e)
        {
            var execute = Observable.Return(string.Empty);

            var projects = from p in this.boundProjects
                           where p.Invoice
                           select p;

            foreach (MessageProject project in projects)
            {
                var currentProject = project;
                var temp = execute;
                execute = from t in temp
                          from mark in this.ViewModel.MarkProjectAsBilled.TryInvoke(currentProject.Project.ProjectID)
                          .Do(message =>
                              {
                                  var boundProject = this.boundProjects.SingleOrDefault(p => p.Project.ProjectID == currentProject.Project.ProjectID);
                                  if (boundProject != null)
                                  {
                                      boundProject.Message = message;
                                      boundProject.Invoice = false;
                                  }
                              })
                          select mark;
            }

            this.Busy.BusyContent = "Setting billed dates...";
            this.Busy.IsBusy = true;

            execute.Subscribe(result => this.Busy.IsBusy = false);
        }

        #endregion Events

        #region Methods

        private void OpenEditor(Func<Action, UIElement> editorCreator)
        {
            if (!this.isEditorOpen)
            {
                ChildWindow cw = new ChildWindow();
                cw.HasCloseButton = false;
                cw.Title = "Edit Project";

                UIElement editor = editorCreator.TryInvoke(cw.Close);

                if (editor == null)
                    return;

                cw.Closed += (sender, e) => this.isEditorOpen = false;

                cw.Content = editor;
                cw.Show();
                this.isEditorOpen = true;
            }
        }

        #endregion Methods

        #region IView<IInvoicingViewModelConsume> Members

        public IInvoicingViewModelConsume ViewModel
        {
            get { return this.DataContext as IInvoicingViewModelConsume; }
            set { this.DataContext = value; }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            this.compositeDisposable.Dispose();
            GC.SuppressFinalize(this);
        }

        #endregion

        ~InvoicingView()
        {
            this.Dispose();
        }
    }
}
